package com.afterglow.lcsrpc.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.afterglow.lcsrpc.RpcApplication;
import com.afterglow.lcsrpc.config.RpcConfig;
import com.afterglow.lcsrpc.constant.RpcConstant;
import com.afterglow.lcsrpc.fault.retry.RetryStrategy;
import com.afterglow.lcsrpc.fault.retry.RetryStrategyFactory;
import com.afterglow.lcsrpc.fault.tolerant.TolerantStrategy;
import com.afterglow.lcsrpc.fault.tolerant.TolerantStrategyFactory;
import com.afterglow.lcsrpc.loadbalancer.LoadBalancer;
import com.afterglow.lcsrpc.loadbalancer.LoadBalancerFactory;
import com.afterglow.lcsrpc.model.RpcRequest;
import com.afterglow.lcsrpc.model.RpcResponse;
import com.afterglow.lcsrpc.model.ServiceMetaInfo;
import com.afterglow.lcsrpc.protocol.*;
import com.afterglow.lcsrpc.registry.Registry;
import com.afterglow.lcsrpc.registry.RegistryFactory;
import com.afterglow.lcsrpc.serializer.Serializer;
import com.afterglow.lcsrpc.serializer.SerializerFactory;
import com.afterglow.lcsrpc.server.tcp.VertxTcpClient;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

/**
 * Description:服务代理（JDK 动态代理）
 * Date: 2024/12/08
 * author: lcs
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
        Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        // 构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 序列化
            byte[] bodyBytes = serializer.serializer(rpcRequest);
            // 发送请求
            // todo 注意，这里地址被硬编码了（需要使用注册中心和服务发现机制解决）
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(rpcRequest.getServiceName());
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }
            // 暂时先取第一个
            //ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);

            LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
            //将调用方法名（请求路口）作为负载均衡参数
            HashMap<String, Object> requestParams = new HashMap<>();
            requestParams.put("methodName", rpcRequest.getMethodName());
            ServiceMetaInfo select = loadBalancer.select(requestParams, serviceMetaInfoList);

            log.info("获取到的负载均衡服务器：{}",select);

            //发送TCP请求
            //RpcResponse rpcResponse = VertxTcpClient.doRequest(rpcRequest, select);

            //使用重试机制
            RpcResponse rpcResponse;
            try {
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                rpcResponse = retryStrategy.doRetry(() ->
                            VertxTcpClient.doRequest(rpcRequest, select)
                );
            } catch (Exception e) {
                //容错机制
                TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                rpcResponse = tolerantStrategy.doTolerant(null, e);
            }
            return rpcResponse.getData();
        } catch (IOException e) {
            throw new RuntimeException("调用失败");
        }
    }
}
